<?php
/**
 * Class for Sitemap Generator
 */
class SQ_Sitemap extends SQ_FrontController {
    /*@var string file path */
    var $file;
    var $opt = array();
    var $data = array();
    var $args = array();
    
     function __construct() {
        if (!isset(SQ_Tools::$options['sq_use']) || SQ_Tools::$options['sq_use'] == 0) return;
        
        $this->filename = 'sitemap.xml';
        
        $this->file = ABSPATH . $this->filename;
        //For sitemap ping
        $this->args['timeout'] = 10;
        $this->opt = array( 'home' => array(1,'daily'),
                            'page' => array(0.6,'monthly'),
                            'post' => array(0.6,'monthly'),
                            'static' => array(0.6,'weekly'),
                            'category' => array(0.4,'monthly'),
                            'archive' => array(0.3,'daily'),
                            'oldarchive' => array(0.3,'monthly'),
                            'tag' => array(0.3,'weekly'),
                            'author' => array(0.3,'weekly'),
                           );
        
        
        
        //Existing posts was deleted
        add_action('delete_post', array($this, 'generateSitemap'),9999,1);
        //Existing post was published
        add_action('publish_post', array($this, 'generateSitemap'),9999,1);
        //Existing page was published
        add_action('publish_page', array($this, 'generateSitemap'),9999,1);
    }
    
    function init() { return; }
    function action() {}
   
    /**
     * Generate the XML sitemap for google
     */
    public function generateSitemap(){
        global $wpdb, $wp_query, $wp_version;
        /* get the home url */
        $home = get_bloginfo('url');
        $homeID = 0;
        $wpCompat = (floatval($wp_version) < 2.1);
        
        /* If the site starts with a page */
        if(get_option('show_on_front') == 'page' && get_option('page_on_front')) {
            $page = get_page(get_option('page_on_front'));
            if($page) {
                $homeID = $page->ID;
                $lastmod = ($page->post_modified_gmt && $page->post_modified_gmt!='0000-00-00 00:00:00' ? $page->post_modified_gmt : $page->post_date_gmt);
                $this->addLine($home, $this->getTimestamp($lastmod), $this->opt['home'][1], $this->opt['home'][0]);
            }
        } else $this->addLine($home, $this->getTimestamp(get_lastpostmodified('GMT')), $this->opt['home'][1], $this->opt['home'][0]);
        
       /***********************************************************************/
       //Add the pages
       $query = array();
        
        /* CREATE QUERY */
        $query['what'] = "`ID`, `post_author`, `post_date`, `post_date_gmt`, `post_status`, `post_name`, `post_modified`, `post_modified_gmt`, `post_parent`, `post_type` ";
        
        /* from */
        $query['from'] = " `" . $wpdb->posts . "` " ;
        /* where */
        $query['where'] = '(';
      
        
        if($wpCompat) 
            $query['where'] .= "(post_status = 'publish' AND post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "')";
        else
            $query['where'] .= "(post_status = 'publish' AND (post_type = 'post' OR post_type = '')) ";
        
        $query['where'] .= " OR ";
        if($wpCompat) 
            $query['where'] .= " post_status='static' ";
        else 
            $query['where'] .= " (post_status = 'publish' AND post_type = 'page') ";

        $query['where'] .= ") ";    
        $query['where'] .= " AND post_password=''";
        /* order */
        $query['order'] = " ORDER BY post_modified DESC";
        /* limit */
        $query['limit'] = ((int)$this->posts_limit > 0) ? " LIMIT 0," . $this->posts_limit : '';
        
        
        $posts = $wpdb->get_results('SELECT '.$query['what'].' FROM '.$query['from'].' WHERE '.$query['where'].' '.$query['order'].' '.$query['limit'].' ');
        if(!$posts) {
                trigger_error( ucfirst(_PLUGIN_NAME_) . " failed to connect to database: " . mysql_error(), E_USER_NOTICE); //E_USER_NOTICE will be displayed on our debug mode
                return;
        }
        
        /* loop */
        foreach ($posts as $post) {
            $out = array();
            $permalink = get_permalink($post->ID);
            if($permalink != $home && $post->ID != $homeID) {

                $isPage = false;
                if($wpCompat)
                    $isPage = ($post->post_status == 'static');
                else
                    $isPage = ($post->post_type == 'page');
                
                if($isPage){
                    $out['priority'] = $this->opt['page'][0];
                    $out['changefreq'] = $this->opt['page'][1];
                }else{
                    $out['priority'] = $this->opt['post'][0];
                    $out['changefreq'] = $this->opt['post'][1];
                }
                $out['lastmod'] = ($post->post_modified_gmt && $post->post_modified_gmt!='0000-00-00 00:00:00' ? $post->post_modified_gmt : $post->post_date_gmt);

                //Add it
                $this->addLine($permalink, $this->getTimestamp($out['lastmod']), $out['changefreq'], $out['priority']);

                $subPage = '';
                for($i = 1; $i <= $post->postPages; $i++) {
                    if(get_option('permalink_structure') == '') {
                        $subPage = $permalink . '&amp;page=' . ($p+1);
                    } else {
                        $subPage = trailingslashit($permalink) . user_trailingslashit($p+1, 'single_paged');
                    }

                    $this->addLine($subPage, $this->getTimestamp($out['lastmod']), $out['changefreq'], $out['priority']);
                }

            }
        }
        
        /***********************************************************************/
        /* Add links ftom categories */
        if(!$this->IsTaxonomySupported()) {
            $query = array();

            /* CREATE QUERY */
            $query['what'] = "c.cat_ID AS ID, MAX(p.post_modified_gmt) AS last_mod";
            $query['from'] = "`" . $wpdb->categories . "` c, `" . $wpdb->post2cat . "` pc, `" . $wpdb->posts . "` p";
            $query['where'] = "pc.category_id = c.cat_ID AND p.ID = pc.post_id AND p.post_status = 'publish' AND p.post_type='post'";
            $query['order'] = "";
            $query['limit'] = "";
            $query['group'] = "GROUP BY c.cat_id";

            $categories= $wpdb->get_results("SELECT ".$query['what']." FROM ".$query['from']." WHERE ".$query['where']." ".$query['order']." ".$query['group']." ".$query['limit']."");

            if($categories) {
                foreach($categories as $category) {
                    if($category && $category->ID && $category->ID > 0 ) {
                        $this->addLine(get_category_link($category->ID),$this->getTimestamp($category->last_mod),$this->opt['category'][1],$this->opt['category'][0]);
                    }
                }
            }
        } else {
            $categories = get_terms("category", array("hide_empty"=>true, "hierarchical"=>false));
            if($categories && is_array($categories) && count($categories)>0) {
                    foreach($categories AS $category) {
                        $this->addLine(get_category_link($category->term_id),0,$this->opt['category'][1],$this->opt['category'][0]);
                    }
            }
        }
        
         /***********************************************************************/
       //Add the archives
        $now = current_time('mysql');
        $query = array();

        /* CREATE QUERY */
        $query['what'] = 'YEAR(post_date_gmt) AS `year`, MONTH(post_date_gmt) AS `month`, MAX(post_date_gmt) as last_mod, count(ID) as posts';
        $query['from'] = "`" . $wpdb->posts . "`";
        $query['where'] = "post_date < '$now' AND post_status = 'publish' AND post_type = 'post' " . (floatval($wp_version) < 2.1 ? "AND {$wpdb->posts}.post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "'" : "") . "";
        $query['order'] = "ORDER BY post_date_gmt DESC";
        $query['limit'] = "";
        $query['group'] = "GROUP BY YEAR(post_date_gmt),MONTH(post_date_gmt)";

        $archives= $wpdb->get_results("SELECT DISTINCT ".$query['what']." FROM ".$query['from']." WHERE ".$query['where']." ".$query['order']." ".$query['group']." ".$query['limit']."");

       
        if ($archives) {
            foreach ($archives as $archive) {

                $changeFreq="";
                //Archive is the current one
                if($archive->month==date("n") && $archive->year==date("Y")) {
                    $changeFreq = $this->opt['archive'][1];
                } else { // Archive is older
                    $changeFreq = $this->opt['oldarchive'][1];
                }

                $this->addLine(get_month_link($archive->year,$archive->month), $this->getTimestamp($archive->last_mod),$changeFreq,$this->opt['archive'][0]);
            }
        }
        
        /***********************************************************************/
        //Add the author pages
        $linkFunc = null;
			
        //get_author_link is deprecated in WP 2.1, try to use get_author_posts_url first.
        if(function_exists('get_author_posts_url')) 
            $linkFunc = 'get_author_posts_url';
        else if(function_exists('get_author_link')) 
            $linkFunc = 'get_author_link';
        
        if($linkFunc !== null) {                
            $query = array();

            /* CREATE QUERY */
            $query['what'] = 'u.ID, u.user_nicename, MAX(p.post_modified_gmt) AS last_post';
            $query['from'] = "`" . $wpdb->users . "` u, `" . $wpdb->posts . "` p";
            $query['where'] = "p.post_author = u.ID AND p.post_status = 'publish' AND p.post_type = 'post' AND p.post_password = '' " . (floatval($wp_version) < 2.1?"AND p.post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "'":"") . "";
            $query['order'] = "";
            $query['limit'] = "";
            $query['group'] = "GROUP BY u.ID, u.user_nicename";

            $authors= $wpdb->get_results("SELECT DISTINCT ".$query['what']." FROM ".$query['from']." WHERE ".$query['where']." ".$query['order']." ".$query['group']." ".$query['limit']."");


            if ($authors && is_array($authors)) {
                foreach ($authors as $author) {

                    $author_url = ($linkFunc=='get_author_posts_url'?get_author_posts_url($author->ID,$author->user_nicename):get_author_link(false,$author->ID,$author->user_nicename));
                    $this->addLine($author_url, $this->getTimestamp($author->last_post),$this->opt['author'][1],$this->opt['author'][0]);
                }
            }
            
        }
        
        /***********************************************************************/
        if($this->IsTaxonomySupported()) {
            $tags = get_terms("post_tag",array("hide_empty"=>true,"hierarchical"=>false));
            if($tags && is_array($tags) && count($tags)>0) {
                foreach($tags AS $tag) {
                    $this->addLine(get_tag_link($tag->term_id),0,$this->opt['tag'][1],$this->opt['tag'][0]);
                }
            }
        }    
        
        return $this->render();

    } # end function  
    
    /**
     * Push new info to array
     * 
     * @param string $link
     * @param string $timestamp
     * @param string $changefreq
     * @param string $priority
     */
    private function addLine($link, $timestamp, $changefreq, $priority){
       
        array_push($this->data, array(
            'loc' => $link,
            'lastmod' => date('Y-m-d\TH:i:s+00:00',$timestamp),
            'changefreq' => $changefreq,
            'priority' => $priority
        ));
    }
    
    /**
     * Create the XML sitemap
     * @return string
     */
    private function render(){
        $content = '';
        $content .= '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        $content .= '<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 
            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" 
            xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
        
        foreach ($this->data as $data){
                $content .= "\t" . '<url>' . "\n";
                $content .= "\t\t" . '<loc>'.$data['loc'].'</loc>' . "\n";
                $content .= "\t\t" . '<lastmod>'.$data['lastmod'].'</lastmod>' . "\n";
                $content .= "\t\t" . '<changefreq>'.$data['changefreq'].'</changefreq>' . "\n";
                $content .= "\t\t" . '<priority>'.$data['priority'].'</priority>' . "\n";
                $content .= "\t" . '</url>' . "\n";
        } # end foreach
        $content .= '</urlset>';
        
        $this->saveSitemap($content);
        
        $this->doPing();
        return $content;
    }
    
    private function doPing(){
        //Ping Google
        $google_url="http://www.google.com/webmasters/sitemaps/ping?sitemap=" . urlencode($this->getXmlUrl());
        $response = wp_remote_get( $google_url, $this->args);
        
        //Ping Bing
        $bing_url="http://www.bing.com/webmaster/ping.aspx?siteMap=" . urlencode($this->getXmlUrl());
        $response = wp_remote_get( $bing_url, $this->args);

    }
    
    /**
    * Converts into a unix timestamp
    * 
    * @param string time
    * @return string
    */
    private function getTimestamp($time) {
           list($date, $hours) = explode(' ', $time);
           list($year,$month,$day) = explode('-',$date);
           list($hour,$min,$sec) = explode(':',$hours);
           return mktime(intval($hour), intval($min), intval($sec), intval($month), intval($day), intval($year));
    }
    /**
     * Function to save the sitemap data to file as either XML or XML.GZ format
     * @param string $data XML data
     * @param string $filename file path
     * 
     * @return boolean
     */	
    private function saveSitemap($data){
            if(function_exists('gzopen') && function_exists('gzwrite') && function_exists('gzclose')){
                if ($gz = gzopen($this->file.'.gz','wb9')){
                        gzwrite($gz, $data);
                        gzclose($gz);
                }
            }
            
            if(function_exists('fopen'))
                if ($fp = @fopen($this->file, 'w+')){
                    fwrite($fp, $data);
                    fclose($fp);
                    return true;
                }
            
       return false;
    } # end function	
  
    /**
    * Returns the URL for the XML sitemap file
    *
    * @return string
    */
    public function getXmlUrl() {
        return trailingslashit(get_bloginfo('url')). $this->filename;
    }    
    
    /**
    * Returns if this version of WordPress supports the new taxonomy system
    *
    * @since 3.0b8
    * @access private
    * @author Arne Brachhold
    * @return true if supported
    */
   private function IsTaxonomySupported() {
           return (function_exists("get_taxonomy") && function_exists("get_terms"));
   }
}

?>
